#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>

#include "dns_server_rr_files.h"
#include "dns_packet_parsing.h"
#include "dns_util.h"

/*
 * Main
 */
int main(int argc, char ** argv) {

	/* Arguments */
	char * rr_file_path;
	int server_port;

	/* Read arguments */
	if (argc != 3) {
		fprintf(stderr, "Usage: %s rr_file_path server_port\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	rr_file_path = argv[1];
	server_port = atoi(argv[2]);

	/* --- */

	SOCKET server_socket = INVALID_SOCKET;
	SOCKADDR_IN server_addr;
	socklen_t server_addr_len;
	SOCKADDR_IN client_addr;
	socklen_t client_addr_len;

	/* Read server type */
	char server_domain[DOMAIN_STR_LENGTH];
	int server_type = read_server_type_from_conf(rr_file_path, server_domain);
	if (server_type != AUTHORITATIVE)
		exit_runerr("Wrong rr file, not authoritative", EXIT_FAILURE, NULL);

	/* Create socket  */
	if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
		exit_syserr("socket()", NULL);

	/* Init server address */
	server_addr_len = sizeof(server_addr);
	memset(&server_addr, 0, server_addr_len);
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(server_port);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	/* Bind socket */
	if (bind(server_socket, (SOCKADDR *) &server_addr, server_addr_len) == SOCKET_ERROR)
		exit_syserr("bind()", &server_socket, NULL);

	/* Run server loop */
	char buffer[BUF_SIZE];
	int msg_length;
	while (true) {
		/* Reset buffer */
		memset(buffer, 0, BUF_SIZE);

		/* Wait for a request */
		printf("\nWaiting...\n");

		/* Read the message */
		if (recvfrom(server_socket, buffer, BUF_SIZE, 0, (SOCKADDR *) &client_addr, &client_addr_len) < 0)
			exit_syserr("recvfrom()", &server_socket, NULL);
		printf("Message received\n");

		/* Extract requested name */
		char qname[DOMAIN_STR_LENGTH];
		char * name;
		if (qname_from_question(buffer, BUF_SIZE, qname) < 0) {
			fprintf(stderr, "Cannot read qname\n");
			continue;
		}
		printf("QName: %s\n", qname);

		name = strtok(qname, ".");
		printf("Name: %s\n", name);

		/* Check domain */
		char * domain = &(qname[strlen(name) + 1]);
		if (strcmp(domain, server_domain)) {
			fprintf(stderr, "Domain does not match: '%s' != '%s'\n", domain, server_domain);

			// Build and send error
			msg_length = build_dns_reply_error(buffer, 8);
			sendto(server_socket, buffer, msg_length, 0, (SOCKADDR *) &client_addr, client_addr_len);
			continue;
		}

		/* Look for a record */
		char ip_addr_str[IPV4_STR_LENGTH];
		if (!get_rr_data_from_name(rr_file_path, name, ip_addr_str)) {
			fprintf(stderr, "No record found\n");

			// Build and send error
			msg_length = build_dns_reply_error(buffer, 3);
			sendto(server_socket, buffer, msg_length, 0, (SOCKADDR *) &client_addr, client_addr_len);
			continue;
		}

		/* Record found, send address */
		printf("HOST: %s\n", ip_addr_str);

		unsigned char ip_addr[4];
		ip_addr[0] = atoi(strtok(ip_addr_str, "."));
		ip_addr[1] = atoi(strtok(NULL, "."));
		ip_addr[2] = atoi(strtok(NULL, "."));
		ip_addr[3] = atoi(strtok(NULL, "."));

		// Build and send answer
		msg_length = build_dns_answer(buffer, ip_addr);
		sendto(server_socket, buffer, msg_length, 0, (SOCKADDR *) &client_addr, client_addr_len);
	}

	/* --- */

	close_socket(&server_socket);

	return EXIT_SUCCESS;
}
